package inputprocessor;

import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.*;

/**
 * Uses the Xerces parser to implement an interface to the XML input file.
 * The XML input file is what the XQuery evaluation will eventually run against.
 * @author RaresMan
 *
 */
public class XMLNavigator
{
   /** 
    * Function to test the interface to our XML input file
    * @param args
    * @throws Exception
    */
	static Document doc;
   public static void main(String[] args) throws Exception
   {
	   String fileName = "input/test.xml";
	   Node root = root(fileName);
	   Node parent = root.getParentNode();
	   Node parent2 = parent.getParentNode();
	  
	   //System.out.println("Root node name: " + root.getNodeName());
	   //System.out.println("Root node type: " + root.getNodeValue());
	   
	   
	   
	   if(parent.getNodeType() == Node.DOCUMENT_NODE) {
		   System.out.println("IsRoot!");
		   System.out.println(parent.getChildNodes().getLength());
	   }
	   
//	   System.out.println(parent.getNodeName() + " " + parent.getNodeType());
//	   System.out.println(root.getNodeName() + " " + root.getNodeType());
	   printNonTextChildren(root, " ");
	   
	   System.out.println("Test descendantOrSelf:");
	   List<Node> test = descendantOrSelf(root);
	   for(Node testNode : test) {
		   System.out.println(testNode.getNodeName());
	   }
	   
//	   NodeList rootChildren = root.getChildNodes();
//	   for(int i=0;i<rootChildren.getLength();i++) {
//		   Node child = rootChildren.item(i);
//		   if(child.getNodeType() == Node.TEXT_NODE) {
//			   //System.out.println("\tchildtext:" + child.toString());
//			   continue;
//		   }
//		   System.out.println("\t" + child.getNodeName());
//	   }
	   
//	   System.out.println( " root children size" + rootChildren.size());
//	   for(Node child : rootChildren) {
//		   if(child.getNodeType() == Node.ELEMENT_NODE) {
//			   //System.out.println("\t" + child.getNodeName());
//			   System.out.println("\t" + tag(child));
//			   System.out.println("\t\t" + text(child));			  
//		   }
//	   }	         
   }

   
	public static void printNonTextChildren(Node n, String prefix) {
		
		//System.out.println(prefix + n.getNodeName() + " " + n.getNodeType());
		
		if(!n.hasChildNodes()) {
			return;
		} else {		
			NodeList children = n.getChildNodes();
			for (int i = 0; i < children.getLength(); i++) {
				Node child = children.item(i);				
				if (child.getNodeType() == Node.TEXT_NODE) {
					System.out.println(prefix + child.getNodeValue());
					continue;
				}
				System.out.println(prefix + " " + child.getNodeName() + " " + child.getNodeType());
				printNonTextChildren(child, prefix + " ");
			}
		}
	}

	public static List<Node> descendantOrSelf(Node n) {
		
		List<Node> resultList = new ArrayList<Node>();
		
		// Add itself to the list
		if ( n.getNodeType() != Node.TEXT_NODE)
		{
			resultList.add(n);

			if (!n.hasChildNodes()) {
				return resultList;
			} else {
				NodeList children = n.getChildNodes();
				for (int i = 0; i < children.getLength(); i++) {
					Node child = children.item(i);
					if (child.getNodeType() == Node.TEXT_NODE) {
						continue;
					}
					resultList.addAll(descendantOrSelf(child));
				}
			}
		}
		return resultList;
	}

	
	public static Document getNewDocument( )
	{
		try {
		       DocumentBuilderFactory factory = 
		       DocumentBuilderFactory.newInstance();
		       DocumentBuilder builder = factory.newDocumentBuilder();
		       DOMImplementation impl = builder.getDOMImplementation();
		       
		      
		       return impl.createDocument(null, null, null);
		   } catch (Exception e) {
			   e.printStackTrace();
			   return null;
		   }
	}
   /**
    * Given a filename to an XML document, reads the document in and returns
    * the root element of the document.
    * @param fileName
    * @return
    */
   public static Node root(String fileName) {

	   String xmlFile = fileName; //"file:///xerces-2_9_1/data/personal.xml"; 
	   try {
	       DocumentBuilderFactory factory = 
	       DocumentBuilderFactory.newInstance();
	       DocumentBuilder builder = factory.newDocumentBuilder();
	       Document document = builder.parse(xmlFile);
	       doc = document;
	       Node n = doc.getDocumentElement();
	       return n;
	   } catch (Exception e) {
		   e.printStackTrace();
		   return null;
	   }
	   
	  
	   
//	   catch (FactoryConfigurationError e) {
//	       // unable to get a document builder factory
//	   } 
//	   catch (ParserConfigurationException e) {
//	       // parser was unable to be configured
//	   }
//	   catch (SAXException e) {
//	       // parsing error
//	   } 
//	   catch (IOException e) {
//	       // i/o error
//	   }
	   
//	   try {
//          DOMParser p = new DOMParser();
//          p.parse(fileName);
//          doc = p.getDocument();
//          Node n = doc.getDocumentElement();
//          
//          return n;          
//       } catch (Exception e) {
//    	   e.printStackTrace(); 
//    	   return null;
//       }
   }

   /**
    * Returns a list of the children of the specified nodes.
    * TODO@Rares: Check to make sure this is in document order
    * @param n
    * @return
    */
   public static List<Node> children(Node n) {

	   
	   NodeList propList = n.getChildNodes();
	   
	   List<Node> normalList = new ArrayList<Node>();
	   
	   for(int i=0;i<propList.getLength();i++) {
		   Node childNode = propList.item(i);
		   if (childNode.getNodeType() == Node.TEXT_NODE)
			   continue;
		   //System.out.println(childNode.toString() );
		   normalList.add(childNode);
	   }

	   return normalList;
   }
   
   /**
    * Given a node n, returns a list containing the parent of that node, or
    * if the node has no parent, an empty list.
    * @param n
    * @return
    */
   public static List<Node> parent(Node n) {
	   
	   List<Node> parentList = new ArrayList<Node>();
	   
	   if(n.getParentNode() != null) {
		   parentList.add(n.getParentNode());
	   } 
	   
	   return parentList;
   }
   
   /**
    * Returns the tag associated with the specified node. Throws an exception 
    * if the node in question is not an element node.
    * @param n
    * @return
    * @throws Exception
    */
   public static String tag(Node n) throws Exception {
	   if(n.getNodeType() != Node.TEXT_NODE) {
		   return n.getNodeName();
	   } else {
		   throw new Exception("Tried to retrieve the tag of a text node");
	   }
   }
   
   /**
    * Returns the text associated with the specified element node. 
    * TODO@Rares: Make sure this isn't supposed to return the actual text node
    * @param n
    * @return
    * @throws Exception
    */
   public static Node text(Node n) throws Exception {
	   if(n.getNodeType() == Node.ELEMENT_NODE) {
		   return n.getFirstChild();
	   } else {
		   throw new Exception("Node is not an element node");
	   }
   }
   
   public static Node makeText(String text)
   {
	   
	   return doc.createTextNode(text);
	   
   }
   
   public static Node makeElem( String tag, List<Node> children)
   {
	    Element new_element = doc.createElement(tag);
	    for ( Node child : children)
	    	new_element.appendChild(doc.importNode(child, true));
	    return new_element;
   }
}